home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / UIDefaults.java < prev    next >
Text File  |  1998-06-30  |  14KB  |  443 lines

  1. /*
  2.  * @(#)UIDefaults.java    1.17 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing;
  22.  
  23.  
  24. import com.sun.java.swing.plaf.ComponentUI;
  25. import com.sun.java.swing.border.Border;
  26.  
  27. import java.util.Hashtable;
  28. import java.awt.Font;
  29. import java.awt.Color;
  30. import java.lang.reflect.Method;
  31. import java.beans.PropertyChangeSupport;
  32. import java.beans.PropertyChangeListener;
  33. import java.beans.PropertyChangeEvent;
  34.  
  35.  
  36. /**
  37.  * A table of defaults for Swing components.  Applications can set/get
  38.  * default values via the UIManager.  
  39.  * <p>
  40.  * Warning: serialized objects of this class will not be compatible with
  41.  * future swing releases.  The current serialization support is appropriate
  42.  * for short term storage or RMI between Swing1.0 applications.  It will
  43.  * not be possible to load serialized Swing1.0 objects with future releases
  44.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  45.  * baseline for the serialized form of Swing objects.
  46.  * 
  47.  * @see UIManager
  48.  * @version 1.17 04/09/98
  49.  * @author Hans Muller
  50.  */
  51. public class UIDefaults extends Hashtable
  52. {
  53.     private static final Object PENDING = new String("Pending");
  54.  
  55.     private PropertyChangeSupport changeSupport;
  56.  
  57.  
  58.     /**
  59.      * Create an empty defaults table.
  60.      */
  61.     public UIDefaults() {
  62.         super();
  63.     }
  64.  
  65.  
  66.     /**
  67.      * Create a defaults table initialized with the specified
  68.      * key/value pairs.  For example:
  69.      * <pre>
  70.         Object[] uiDefaults = {
  71.              "Font", new Font("Dialog", Font.BOLD, 12),
  72.             "Color", Color.red,
  73.              "five", new Integer(5)
  74.         }
  75.         UIDefaults myDefaults = new UIDefaults(uiDefaults);
  76.      * </pre>
  77.      */
  78.     public UIDefaults(Object[] keyValueList) {
  79.         super(keyValueList.length / 2);
  80.         for(int i = 0; i < keyValueList.length; i += 2) {
  81.             super.put(keyValueList[i], keyValueList[i + 1]);
  82.         }
  83.     }
  84.  
  85.  
  86.     /*
  87.      * Returns the value for key.  If the value is a 
  88.      * <code>UIDefaults.LazyValue</code> then the real
  89.      * value is computed with <code>LazyValue.createValue()</code>, 
  90.      * the table entry is replaced, and the real value is returned.
  91.      * If the value is an <code>UIDefaults.ActiveValue</code>
  92.      * the table entry is not replaced - the value is computed
  93.      * with ActiveValue.createValue() for each get() call.
  94.      * 
  95.      * @see LazyValue
  96.      * @see ActiveValue
  97.      * @see java.util.Hashtable#get
  98.      */
  99.     public Object get(Object key) 
  100.     {
  101.         /* Quickly handle the common case, without grabbing 
  102.          * a lock.
  103.          */
  104.         Object value = super.get(key);
  105.         if ((value != PENDING) && 
  106.             !(value instanceof ActiveValue) &&
  107.             !(value instanceof LazyValue)) {
  108.             return value;
  109.         }
  110.  
  111.         /* If the LazyValue for key is being constructed by another 
  112.          * thread then wait and then return the new value, otherwise drop
  113.          * the lock and construct the ActiveValue or the LazyValue.
  114.          * We use the special value PENDING to mark LazyValues that
  115.          * are being constructed.
  116.          */
  117.         synchronized(this) {
  118.             value = super.get(key);
  119.             if (value == PENDING) {
  120.                 do {
  121.                     try {
  122.                         this.wait();
  123.                     }
  124.                     catch (InterruptedException e) {
  125.                     }
  126.                     value = super.get(key);
  127.                 } 
  128.                 while(value == PENDING);
  129.                 return value;
  130.             }
  131.             else if (value instanceof LazyValue) {
  132.                 super.put(key, PENDING);
  133.             }
  134.             else if (!(value instanceof ActiveValue)) {
  135.                 return value;
  136.             }
  137.         }
  138.  
  139.         /* At this point we know that the value of key was
  140.          * a LazyValue or an ActiveValue.
  141.          */
  142.         if (value instanceof LazyValue) {
  143.             try {
  144.                 /* If an exception is thrown we'll just put the LazyValue
  145.                  * back in the table. 
  146.                  */
  147.                 value = ((LazyValue)value).createValue(this);
  148.             }
  149.             finally {
  150.                 synchronized(this) {
  151.                     if (value == null) {
  152.                         super.remove(key);
  153.                     }
  154.                     else {
  155.                         super.put(key, value);
  156.                     }
  157.                     this.notify();
  158.                 }
  159.             }
  160.         }
  161.         else {
  162.             value = ((ActiveValue)value).createValue(this);
  163.         }
  164.         
  165.         return value;
  166.     }
  167.  
  168.  
  169.     /**
  170.      * Set the value of <code>key</code> to <code>value</code>.
  171.      * If <code>key</code> is a string and the new value isn't
  172.      * equal to the old one, fire a PropertyChangeEvent.  If value
  173.      * is null, the key is removed from the table.
  174.      * 
  175.      * @see #putDefaults
  176.      * @see java.util.Hashtable#put
  177.      */
  178.     public Object put(Object key, Object value) {
  179.         Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
  180.         if (key instanceof String) {
  181.             firePropertyChange((String)key, oldValue, value);
  182.         }
  183.         return oldValue;
  184.     }
  185.  
  186.  
  187.     /**
  188.      * Put all of the key/value pairs in the database and
  189.      * unconditionally generate one PropertyChangeEvent.
  190.      * The events oldValue and newValue will be null and its 
  191.      * propertyName will be "UIDefaults".
  192.      * 
  193.      * @see #put
  194.      * @see java.util.Hashtable#put
  195.      */
  196.     public void putDefaults(Object[] keyValueList) {
  197.         for(int i = 0; i < keyValueList.length; i += 2) {
  198.             Object value = keyValueList[i + 1];
  199.             if (value == null) {
  200.                 super.remove(keyValueList[i]);
  201.             }
  202.             else {
  203.                 super.put(keyValueList[i], value);
  204.             }
  205.         }
  206.         firePropertyChange("UIDefaults", null, null);
  207.     }
  208.  
  209.  
  210.     /**
  211.      * If the value of <code>key</code> is a Font return it, otherwise
  212.      * return null.
  213.      */
  214.     public Font getFont(Object key) {
  215.         Object value = get(key);
  216.         return (value instanceof Font) ? (Font)value : null;
  217.     }
  218.  
  219.     /**
  220.      * If the value of <code>key</code> is a Color return it, otherwise
  221.      * return null.
  222.      */
  223.     public Color getColor(Object key) {
  224.         Object value = get(key);
  225.         return (value instanceof Color) ? (Color)value : null;
  226.     }
  227.  
  228.  
  229.     /**
  230.      * If the value of <code>key</code> is an Icon return it, otherwise
  231.      * return null.
  232.      */
  233.     public Icon getIcon(Object key) {
  234.         Object value = get(key);
  235.         return (value instanceof Icon) ? (Icon)value : null;
  236.     }
  237.  
  238.  
  239.     /**
  240.      * If the value of <code>key</code> is a Border return it, otherwise
  241.      * return null.
  242.      */
  243.     public Border getBorder(Object key) {
  244.         Object value = get(key);
  245.         return (value instanceof Border) ? (Border)value : null;
  246.     }
  247.  
  248.  
  249.     /**
  250.      * If the value of <code>key</code> is a String return it, otherwise
  251.      * return null.
  252.      */
  253.     public String getString(Object key) {
  254.         Object value = get(key);
  255.         return (value instanceof String) ? (String)value : null;
  256.     }
  257.  
  258.  
  259.     /**
  260.      * The value of get(uidClassID) must be the String name of a 
  261.      * class that implements the corresponding ComponentUI 
  262.      * class.  This method looks up the class with 
  263.      * <code>classForName()</code> and returns it.  If no
  264.      * mapping for uiClassID exists or if the specified
  265.      * class can't be found, return null.
  266.      * <p>
  267.      * This method is used by <code>getUI</code>, it's usually
  268.      * not neccessary to call it directly.
  269.      * 
  270.      * @return The value of <code>Class.forName(get(uidClassID))</code>.
  271.      * @see #getUI
  272.      */
  273.     public Class getUIClass(String uiClassID) 
  274.     {
  275.         Object className = get(uiClassID);
  276.         try {
  277.             return (className instanceof String) ? Class.forName((String)className) : null;
  278.         }
  279.         catch(ClassNotFoundException e) {
  280.             return null;
  281.         }
  282.     }
  283.  
  284.  
  285.     /** 
  286.      * If getUI() fails for any reason, it calls this method before
  287.      * returning null.  Subclasses may choose to do more or
  288.      * less here.
  289.      * 
  290.      * @param msg Message string to print.
  291.      * @see #getUI
  292.      */
  293.     protected void getUIError(String msg) {
  294.         System.err.println("UIDefaults.getUI() failed: " + msg);
  295.         try {
  296.             throw new Error();
  297.         }
  298.         catch (Throwable e) {
  299.             e.printStackTrace();
  300.         }
  301.     }
  302.  
  303.  
  304.     /**
  305.      * Create an ComponentUI implementation for the 
  306.      * specified component.  In other words create the look
  307.      * and feel specific delegate object for <code>target</code>.
  308.      * This is done in two steps:
  309.      * <ul>
  310.      * <li> Lookup the name of the ComponentUI implementation 
  311.      * class under the value returned by target.getUIClassID().
  312.      * <li> Use the implementation classes static <code>createUI()</code>
  313.      * method to construct a look and feel delegate.
  314.      * </ul>
  315.      */
  316.     public ComponentUI getUI(JComponent target)
  317.     {
  318.         Class uiClass = getUIClass(target.getUIClassID());
  319.         Object uiObject = null;
  320.  
  321.         if (uiClass == null) {
  322.             getUIError("no ComponentUI class for: " + target);
  323.         }
  324.         else {
  325.             try {
  326.                 Class acClass = com.sun.java.swing.JComponent.class;
  327.                 Method m = uiClass.getMethod("createUI", new Class[]{acClass});
  328.                 uiObject = m.invoke(null, new Object[]{target});
  329.             }
  330.             catch (NoSuchMethodException e) {
  331.                 getUIError("static createUI() method not found in " + uiClass);
  332.             }
  333.             catch (Exception e) {
  334.                 getUIError("createUI() failed for " + target + " " + e);
  335.             }
  336.         }
  337.  
  338.         return (ComponentUI)uiObject;
  339.     }
  340.  
  341.  
  342.     /**
  343.      * Add a PropertyChangeListener to the listener list.
  344.      * The listener is registered for all properties.
  345.      * <p>
  346.      * A PropertyChangeEvent will get fired whenever a default
  347.      * is changed.
  348.      *
  349.      * @param listener  The PropertyChangeListener to be added
  350.      * @see java.beans.PropertyChangeSupport
  351.      */
  352.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
  353.         if (changeSupport == null) {
  354.             changeSupport = new PropertyChangeSupport(this);
  355.         }
  356.         changeSupport.addPropertyChangeListener(listener);
  357.     }
  358.  
  359.  
  360.     /**
  361.      * Remove a PropertyChangeListener from the listener list.
  362.      * This removes a PropertyChangeListener that was registered
  363.      * for all properties.
  364.      *
  365.      * @param listener  The PropertyChangeListener to be removed
  366.      * @see java.beans.PropertyChangeSupport
  367.      */
  368.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
  369.         if (changeSupport != null) {
  370.             changeSupport.removePropertyChangeListener(listener);
  371.         }
  372.     }
  373.  
  374.  
  375.     /**
  376.      * Support for reporting bound property changes.  If oldValue and 
  377.      * newValue are not equal and the PropertyChangeEvent listener list 
  378.      * isn't empty, then fire a PropertyChange event to each listener.
  379.      * 
  380.      * @param propertyName  The programmatic name of the property that was changed.
  381.      * @param oldValue  The old value of the property.
  382.      * @param newValue  The new value of the property.
  383.      * @see java.beans.PropertyChangeSupport
  384.      */
  385.     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
  386.         if (changeSupport != null) {
  387.             changeSupport.firePropertyChange(propertyName, oldValue, newValue);
  388.         }
  389.     }
  390.  
  391.  
  392.     /**
  393.      * This class enables one to store an entry in the defaults
  394.      * table that isn't constructed until the first time it's 
  395.      * looked up with one of the <code>getXXX(key)</code> methods.
  396.      * Lazy values are useful for defaults that are expensive
  397.      * to construct or are seldom retrieved.  The first time
  398.      * a LazyValue is retrieved its "real value" is computed
  399.      * by calling <code>LazyValue.createValue()</code> and the real
  400.      * value is used to replace the LazyValue in the UIDefaults
  401.      * table.  Subsequent lookups for the same key return 
  402.      * the real value.  Here's an example of a LazyValue that 
  403.      * constructs a Border:
  404.      * <pre>
  405.      *  Object borderLazyValue = new UIDefaults.LazyValue() {
  406.      *      public Object createValue(UIDefaults table) { 
  407.      *          return new BorderFactory.createLoweredBevelBorder();
  408.      *      }
  409.      *  };
  410.      *
  411.      *  uiDefaultsTable.put("MyBorder", borderLazyValue);
  412.      * </pre>
  413.      * 
  414.      * @see UIDefaults#get
  415.      */
  416.     public interface LazyValue {
  417.         Object createValue(UIDefaults table);
  418.     }
  419.  
  420.  
  421.     /**
  422.      * This class enables one to store an entry in the defaults
  423.      * table that's constructed each time it's looked up with one of 
  424.      * the <code>getXXX(key)</code> methods. Here's an example of 
  425.      * an ActiveValue that constructs a BasicListCellRenderer
  426.      * <pre>
  427.      *  Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
  428.      *      public Object createValue(UIDefaults table) { 
  429.      *          return new BasicListCellRenderer();
  430.      *      }
  431.      *  };
  432.      *
  433.      *  uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
  434.      * </pre>
  435.      * 
  436.      * @see UIDefaults#get
  437.      */
  438.     public interface ActiveValue {
  439.         Object createValue(UIDefaults table);
  440.     }
  441. }
  442.  
  443.